"""
市场概览页面渲染模块。
"""
import streamlit as st
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
import datetime

# 从 app_setup 导入 logger
try:
    from .app_setup import logger
except ImportError:
    import logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

def render_market_overview():
    """渲染市场概览界面"""
    st.markdown('<div class="main-header">市场概览</div>', unsafe_allow_html=True)

    # --- 市场概况指标 ---
    render_market_metrics()

    # --- 市场热度图 ---
    render_market_heatmap()

    # --- 行业板块轮动 ---
    render_sector_rotation()

    # --- 市场情绪指标 ---
    render_market_sentiment()


def render_market_metrics():
    """渲染主要市场指数指标"""
    # 从缓存加载实时指数数据
    sh_latest = {"close": 0.0}
    sz_latest = {"close": 0.0}
    cyb_latest = {"close": 0.0}
    sh_change_pct = 0.0
    sz_change_pct = 0.0
    cyb_change_pct = 0.0
    cyb_available = False # 标记创业板数据是否可用

    # 尝试获取真实的指数数据
    if st.session_state.get('data_loaded', False) and 'indices_data' in st.session_state:
        indices_data = st.session_state.indices_data
        sh_index = indices_data.get("上证指数")
        sz_index = indices_data.get("深证成指")
        cyb_index = indices_data.get("创业板指") # 尝试获取创业板数据

        if sh_index is not None and not sh_index.empty and len(sh_index) >= 2:
            sh_index = sh_index.sort_values(by="date", ascending=False)
            sh_latest = sh_index.iloc[0]
            sh_prev = sh_index.iloc[1]
            # 确保价格有效再计算
            if pd.notna(sh_latest.get('close')) and pd.notna(sh_prev.get('close')) and sh_prev.get('close') != 0:
                sh_change_pct = (sh_latest['close'] - sh_prev['close']) / sh_prev['close'] * 100
            else:
                logger.warning("上证指数最新或前一日收盘价无效，无法计算涨跌幅。")
                sh_change_pct = 0.0 # 或其他默认值

        if sz_index is not None and not sz_index.empty and len(sz_index) >= 2:
            sz_index = sz_index.sort_values(by="date", ascending=False)
            sz_latest = sz_index.iloc[0]
            sz_prev = sz_index.iloc[1]
            if pd.notna(sz_latest.get('close')) and pd.notna(sz_prev.get('close')) and sz_prev.get('close') != 0:
                sz_change_pct = (sz_latest['close'] - sz_prev['close']) / sz_prev['close'] * 100
            else:
                logger.warning("深证成指最新或前一日收盘价无效，无法计算涨跌幅。")
                sz_change_pct = 0.0

        # 处理创业板指数
        if cyb_index is not None and not cyb_index.empty and len(cyb_index) >= 2:
            cyb_index = cyb_index.sort_values(by="date", ascending=False)
            cyb_latest = cyb_index.iloc[0]
            cyb_prev = cyb_index.iloc[1]
            if pd.notna(cyb_latest.get('close')) and pd.notna(cyb_prev.get('close')) and cyb_prev.get('close') != 0:
                cyb_change_pct = (cyb_latest['close'] - cyb_prev['close']) / cyb_prev['close'] * 100
                cyb_available = True # 标记数据可用
            else:
                logger.warning("创业板指最新或前一日收盘价无效，无法计算涨跌幅。")
                cyb_change_pct = 0.0
        else:
             logger.info("未加载或创业板指数数据不足。")


    col1, col2, col3 = st.columns(3)
    with col1:
        st.metric(
            label="沪指",
            value=f"{sh_latest.get('close', 0):.2f}",
            delta=f"{sh_change_pct:.2f}%"
        )
    with col2:
        st.metric(
            label="深成指",
            value=f"{sz_latest.get('close', 0):.2f}",
            delta=f"{sz_change_pct:.2f}%"
        )
    with col3:
        if cyb_available:
            st.metric(
                label="创业板指",
                value=f"{cyb_latest.get('close', 0):.2f}",
                delta=f"{cyb_change_pct:.2f}%"
            )
        else:
             # 如果数据不可用，可以显示默认值或提示
             st.metric(
                label="创业板指",
                value=f"{cyb_latest.get('close', 0):.2f}", # 显示默认值
                delta="N/A", # 不显示变化
                help="创业板指数数据未加载或不足"
            )


def render_market_heatmap():
    """渲染市场热度图"""
    st.markdown('<div class="sub-header">市场热度图</div>', unsafe_allow_html=True)
    heat_df = pd.DataFrame() # 初始化
    has_industry_data = False

    # 尝试使用真实股票数据创建热度图
    if st.session_state.get('data_loaded', False) and 'stock_data' in st.session_state and 'indices_data' in st.session_state:
        try:
            stock_list = st.session_state.indices_data.get("stock_list")
            stock_data_dict = st.session_state.get("stock_data", {})
            heat_data = []

            # 检查 stock_list 是否有效
            if stock_list is not None and not stock_list.empty:
                # 检查是否包含 'industry' 列
                if 'industry' in stock_list.columns:
                    has_industry_data = True
                    logger.info("股票列表包含行业信息，按行业生成热度图。")
                    top_industries = stock_list['industry'].value_counts().head(10).index.tolist()

                    for industry in top_industries:
                        industry_stocks = stock_list[stock_list['industry'] == industry]
                        stocks_subset = industry_stocks.head(15) # 限制每个行业的股票数量

                        for _, row in stocks_subset.iterrows():
                            code = str(row['code']).zfill(6) # 确保代码是6位字符串
                            name = row['name']
                            stock_df = stock_data_dict.get(code)

                            if stock_df is not None and not stock_df.empty and len(stock_df) >= 2 and '收盘' in stock_df.columns:
                                stock_df = stock_df.sort_values(by="date", ascending=False)
                                latest = stock_df.iloc[0]
                                prev = stock_df.iloc[1]

                                if pd.notna(latest['收盘']) and pd.notna(prev['收盘']) and prev['收盘'] != 0:
                                    change_pct = (latest['收盘'] - prev['收盘']) / prev['收盘'] * 100
                                    # 模拟市值 - 实际应从基本信息获取
                                    market_cap = latest['收盘'] * 5000000 # 简化计算
                                    heat_data.append({
                                        "sector": industry,
                                        "stock": f"{name}({code})",
                                        "change_pct": change_pct,
                                        "market_cap": market_cap
                                    })
                                else:
                                    logger.debug(f"股票 {name}({code}) 价格数据无效或为零，跳过热度图计算。")
                            # else:
                            #      logger.debug(f"股票 {name}({code}) 数据不足或格式错误，跳过热度图计算。")
                else:
                    logger.info("股票列表缺少行业信息，将随机选择股票生成热度图。")
                    # 如果没有行业信息，随机选择 N 只股票
                    stocks_subset = stock_list.sample(n=min(150, len(stock_list)), random_state=42) # 最多选150只
                    for _, row in stocks_subset.iterrows():
                        code = str(row['code']).zfill(6)
                        name = row['name']
                        stock_df = stock_data_dict.get(code)

                        if stock_df is not None and not stock_df.empty and len(stock_df) >= 2 and '收盘' in stock_df.columns:
                            stock_df = stock_df.sort_values(by="date", ascending=False)
                            latest = stock_df.iloc[0]
                            prev = stock_df.iloc[1]

                            if pd.notna(latest['收盘']) and pd.notna(prev['收盘']) and prev['收盘'] != 0:
                                change_pct = (latest['收盘'] - prev['收盘']) / prev['收盘'] * 100
                                market_cap = latest['收盘'] * 5000000 # 简化计算
                                heat_data.append({
                                    "sector": "全市场", # 使用统一的顶层标签
                                    "stock": f"{name}({code})",
                                    "change_pct": change_pct,
                                    "market_cap": market_cap
                                })
                            # else:
                            #     logger.debug(f"股票 {name}({code}) 价格数据无效或为零，跳过热度图计算。")
                        # else:
                        #      logger.debug(f"股票 {name}({code}) 数据不足或格式错误，跳过热度图计算。")

            # 如果真实数据不足，填充随机数据
            if len(heat_data) < 50:
                logger.info("正在从数据库加载历史数据补充...")
                try:
                    from database.session import get_db_session
                    from database.models import StockDaily
                    
                    with get_db_session() as session:
                        # 获取最近交易日数据
                        latest_date_result = session.query(StockDaily.trade_date)\
                                          .order_by(StockDaily.trade_date.desc())\
                                          .first()
                        
                        if latest_date_result is None:
                            logger.warning("数据库中没有交易日数据")
                            raise Exception("数据库中没有交易日数据")
                            
                        latest_date = latest_date_result[0]
                        
                        hist_data = session.query(StockDaily)\
                                        .filter(StockDaily.trade_date == latest_date)\
                                        .limit(50)\
                                        .all()
                                        
                        for record in hist_data:
                            heat_data.append({
                                "sector": record.industry or "全市场",
                                "stock": f"{record.stock_name}({record.stock_code})",
                                "change_pct": record.change_pct,
                                "market_cap": record.market_cap
                            })
                except Exception as e:
                    logger.error(f"数据库加载失败: {e}")
                    heat_data.extend(generate_random_heatmap_data(50 - len(heat_data), use_sectors=has_industry_data))

            if heat_data:
                heat_df = pd.DataFrame(heat_data)

        except Exception as e:
            logger.error(f"创建热度图数据时出错: {e}", exc_info=True)
            # 出错时使用随机数据
            heat_df = pd.DataFrame(generate_random_heatmap_data(150, use_sectors=False)) # 出错时不用行业

    else:
        # 数据未加载时使用随机数据
        logger.info("初始数据未加载，使用随机热度图数据。")
        heat_df = pd.DataFrame(generate_random_heatmap_data(150, use_sectors=False)) # 未加载时不用行业

    # 创建热度图 (仅当 heat_df 有数据时)
    if not heat_df.empty:
        try:
            # 根据是否有行业数据调整 treemap 路径
            path_list = [px.Constant("全市场"), "sector", "stock"] if has_industry_data else [px.Constant("全市场"), "stock"]

            fig = px.treemap(
                heat_df,
                path=path_list,
                values="market_cap",
                color="change_pct",
                color_continuous_scale="RdBu_r", # 红涨绿跌的反向色阶
                color_continuous_midpoint=0,
                custom_data=['change_pct'] # 添加 change_pct 到悬停数据
            )
            fig.update_traces(
                hovertemplate='<b>%{label}</b><br>市值: %{value}<br>涨跌幅: %{customdata[0]:.2f}%<extra></extra>'
            )
            fig.update_layout(
                height=500,
                margin=dict(t=30, l=10, r=10, b=10) # 调整边距
            )
            st.plotly_chart(fig, use_container_width=True)
        except Exception as e:
            logger.error(f"渲染热度图时出错: {e}", exc_info=True)
            st.warning("无法渲染市场热度图。")
    else:
        st.warning("无可用数据生成市场热度图。")


def generate_random_heatmap_data(count=150, use_sectors=True):
    """生成随机热度图数据"""
    np.random.seed(42)
    sectors = ["科技", "消费", "医疗", "金融", "地产", "能源", "材料", "通信", "公用事业", "工业"]
    heat_data = []
    if use_sectors:
        stocks_per_sector = max(1, count // len(sectors))
        for sector in sectors:
            for i in range(stocks_per_sector):
                stock_name = f"{sector}{i+1}"
                change_pct = np.random.normal(0, 2) # 调整标准差
                market_cap = np.random.randint(10000000, 500000000) # 调整市值范围
                heat_data.append({
                    "sector": sector,
                    "stock": stock_name,
                    "change_pct": change_pct,
                    "market_cap": market_cap
                })
    else: # 不使用行业分组
         for i in range(count):
            stock_name = f"股票{i+1}"
            change_pct = np.random.normal(0, 2)
            market_cap = np.random.randint(10000000, 500000000)
            heat_data.append({
                "sector": "全市场", # 统一顶层
                "stock": stock_name,
                "change_pct": change_pct,
                "market_cap": market_cap
            })
    return heat_data


def render_sector_rotation():
    """渲染行业板块轮动图"""
    st.markdown('<div class="sub-header">行业板块轮动</div>', unsafe_allow_html=True)
    rotation_df = pd.DataFrame() # 初始化

    # 尝试使用真实指数数据趋势
    if st.session_state.get('data_loaded', False) and 'indices_data' in st.session_state:
        try:
            # 使用上证指数作为基准趋势
            sh_index = st.session_state.indices_data.get("上证指数")
            if sh_index is not None and not sh_index.empty:
                sh_index = sh_index.sort_values(by="date")
                recent_data = sh_index.tail(30) # 最近30天

                if len(recent_data) >= 2: # 确保有足够数据计算趋势
                    # 初始化日期列表
                    dates = pd.date_range(end=datetime.datetime.now(), periods=len(recent_data)).strftime("%m-%d").tolist()
                    
                    # 尝试使用真实日期数据
                    try:
                        # 确保日期列是日期时间类型
                        if not pd.api.types.is_datetime64_any_dtype(recent_data['date']):
                            recent_data.loc[:, 'date'] = pd.to_datetime(recent_data['date'])
                        
                        if pd.api.types.is_datetime64_any_dtype(recent_data['date']):
                            dates = recent_data['date'].dt.strftime("%m-%d").tolist()
                            logger.debug("成功使用真实日期数据")
                        else:
                            logger.warning("日期列转换后仍不是datetime类型，使用默认日期")
                    except Exception as e:
                        logger.error(f"处理日期列时出错: {e}, 使用默认日期范围")
                    base_trend = recent_data['close'].values
                    # 归一化基准趋势
                    min_base, max_base = np.min(base_trend), np.max(base_trend)
                    range_base = max_base - min_base
                    # 检查 range_base 是否为零
                    if range_base == 0:
                        logger.warning("基准指数在选定周期内没有波动，无法进行归一化。")
                        base_normalized = np.zeros_like(base_trend) # 或者设为常数
                    else:
                        base_normalized = 100 * (base_trend - min_base) / range_base

                    sectors = ["科技", "消费", "医疗", "金融", "地产", "能源", "材料", "通信", "公用事业", "工业"]
                    sector_data = {}
                    np.random.seed(42)

                    for sector in sectors:
                        # 基于基准趋势添加随机因子模拟不同行业表现
                        if sector in ["科技", "医疗"]: factor = np.random.normal(0.1, 0.1, len(base_normalized))
                        elif sector in ["地产", "金融"]: factor = np.random.normal(-0.05, 0.1, len(base_normalized))
                        else: factor = np.random.normal(0, 0.1, len(base_normalized))

                        trend = base_normalized + factor * 20 # 调整因子影响程度
                        # 再次归一化到 0-100
                        min_val, max_val = np.min(trend), np.max(trend)
                        range_val = max_val - min_val
                        normalized = 100 * (trend - min_val) / range_val if range_val != 0 else trend
                        sector_data[sector] = normalized

                    rotation_df = pd.DataFrame(sector_data, index=dates)
                else:
                    logger.warning("用于行业轮动的指数数据不足，将使用随机数据。")
                    rotation_df = generate_random_rotation_data()
            else:
                logger.warning("无指数数据用于行业轮动，将使用随机数据。")
                rotation_df = generate_random_rotation_data()

        except Exception as e:
            logger.error(f"创建行业板块轮动数据时出错: {e}", exc_info=True)
            rotation_df = generate_random_rotation_data()
    else:
        logger.info("初始数据未加载，使用随机行业轮动数据。")
        rotation_df = generate_random_rotation_data()

    # 创建面积图 (仅当 rotation_df 有数据时)
    if not rotation_df.empty:
        try:
            fig = px.line(
                rotation_df,
                x=rotation_df.index,
                y=rotation_df.columns,
                labels={"index": "日期", "value": "相对强度", "variable": "行业"},
                height=400
            )
            fig.update_layout(
                legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
                margin=dict(t=30, l=10, r=10, b=10) # 调整边距
            )
            st.plotly_chart(fig, use_container_width=True)
        except Exception as e:
            logger.error(f"渲染行业轮动图时出错: {e}", exc_info=True)
            st.warning("无法渲染行业板块轮动图。")
    else:
        st.warning("无可用数据生成行业板块轮动图。")


def generate_random_rotation_data(days=30):
    """生成随机行业轮动数据"""
    dates = pd.date_range(end=datetime.datetime.now(), periods=days).strftime("%m-%d").tolist()
    sectors = ["科技", "消费", "医疗", "金融", "地产", "能源", "材料", "通信", "公用事业", "工业"]
    sector_data = {}
    np.random.seed(42)
    for sector in sectors:
        if sector in ["科技", "医疗"]: base = np.random.normal(0.1, 0.5, days)
        elif sector in ["地产", "金融"]: base = np.random.normal(-0.05, 0.3, days)
        else: base = np.random.normal(0, 0.4, days)
        cumulative = np.cumsum(base)
        min_val, max_val = np.min(cumulative), np.max(cumulative)
        range_val = max_val - min_val
        normalized = 100 * (cumulative - min_val) / range_val if range_val != 0 else cumulative
        sector_data[sector] = normalized
    return pd.DataFrame(sector_data, index=dates)


def render_market_sentiment():
    """渲染市场情绪指标"""
    st.markdown('<div class="sub-header">市场情绪指标</div>', unsafe_allow_html=True)

    # 实时数据默认值
    sentiment_defaults = {
        'fear_greed_index': st.session_state.get('fear_greed', 50),
        'market_breadth': st.session_state.get('market_breadth', 50),
        'volatility_index': st.session_state.get('volatility', 20.0),
        'north_fund': st.session_state.get('northbound', 0.0)
    }
    # 从数据收集器获取变化值
    from database.data_collector import get_realtime_sentiment
    realtime_data = get_realtime_sentiment()
    sentiment = sentiment_defaults.copy() # Start with defaults

    # 尝试从 workflow_result 获取数据
    if st.session_state.get('workflow_result') and "result" in st.session_state.workflow_result:
        result = st.session_state.workflow_result["result"]
        if isinstance(result, dict) and "market_sentiment" in result and isinstance(result["market_sentiment"], dict):
            # Merge workflow results with defaults, preferring workflow results
            sentiment.update(result["market_sentiment"])
            logger.info("使用来自 workflow_result 的市场情绪指标。")
        else:
            logger.info("workflow_result 中无有效的 market_sentiment，使用默认情绪指标。")
    else:
        logger.info("无 workflow_result，使用默认情绪指标。")

    col1, col2, col3, col4 = st.columns(4)
    with col1:
        st.metric(
            label="恐惧与贪婪指数",
            value=f"{sentiment.get('fear_greed_index', sentiment_defaults['fear_greed_index'])}",
            delta=f"{int(sentiment.get('fear_greed_index', 50) - 50)}",
            help="市场情绪指标，0表示极度恐惧，100表示极度贪婪"
        )
    with col2:
        st.metric(
            label="市场宽度",
            value=f"{sentiment.get('market_breadth', sentiment_defaults['market_breadth'])}%",
            delta=f"{int(sentiment.get('market_breadth', 50) - 50)}%",
            help="上涨股票占总交易股票的百分比"
        )
    with col3:
        st.metric(
            label="波动率指数",
            value=f"{sentiment.get('volatility_index', sentiment_defaults['volatility_index'])}",
            delta=f"{sentiment.get('volatility_index', 20.0) - 20.0:.1f}",
            help="市场波动性的量化指标，数值越大表示波动越大"
        )
    with col4:
        st.metric(
            label="北向资金(亿元)",
            value=f"{sentiment.get('north_fund', sentiment_defaults['north_fund'])}",
            delta=f"{sentiment.get('north_fund', 0.0)}",
            help="当日北向资金净流入金额（亿元）"
        )